#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <netinet/udp.h>
#include <arpa/inet.h>

#include <exanic/exanic.h>
#include <exanic/fifo_rx.h>
#include <exanic/fifo_tx.h>
#include <exanic/util.h>

#include "normal_udp.h"

#define ETHTYPE_OFFSET 12
#define DOT1Q_ETHTYPE_OFFSET 16
#define IP_ID_OFFSET 18
#define DST_IP_OFFSET 30
#define PAYLOAD_OFFSET 42
#define IPV4_PACKET 0x0008
#define DOT1Q_FRAME 0x0081
#define DST_IP_FILTER 0xefefef05 // 239.239.239.5 is DST IP we want to read

int main(int argc, char *argv[])
{
	char *nic_name;
	uint8_t nic_port = 0;
	int keep_running = 1;

	int size = 0;

	uint16_t prev_ip_id = 0;
	char packet_buf[2048];
	int header_printed = 0;

	if (argc != 2)
	{
		fprintf(stderr, "Usage: %s exanic[0-N]:[0,1]\n", argv[0]);
		return -1;
	}

	if (strchr(argv[1], ':') != NULL)
	{
		nic_name = strtok(argv[1], ":");
		nic_port = atoi(strtok(NULL, ":"));

		printf("nic_name:%s, nic_port:%d \n", nic_name, nic_port);
	}
	else
	{
		fprintf(stderr, "Usage: %s exanic[0-N]:[0,1]\n", argv[0]);
		return -1;
	}

	/* acquire exanic device handle */
	exanic_t *nic = exanic_acquire_handle(nic_name);
	if (!nic)
	{
		fprintf(stderr, "exanic_acquire_handle: %s\n", exanic_get_last_error());
		return -1;
	}

	/* fpga upload data to port1, acquire rx buffer to receive data */
	exanic_rx_t *rx = exanic_acquire_rx_buffer(nic, nic_port, 0);
	if (!rx)
	{
		fprintf(stderr, "exanic_acquire_rx_buffer: %s\n", exanic_get_last_error());
		return -1;
	}

	while (keep_running)
	{
		size = exanic_receive_frame(rx, packet_buf, sizeof(packet_buf), 0);

		if (size > 64)
		{

			uint16_t *ethtype = (uint16_t *)(packet_buf + ETHTYPE_OFFSET);
			uint16_t *dot1q_ethtype = (uint16_t *)(packet_buf + DOT1Q_ETHTYPE_OFFSET);
			int dot1q_offset = 0;

			if (*ethtype == IPV4_PACKET)
				dot1q_offset = 0;
			else if (*ethtype == DOT1Q_FRAME && *dot1q_ethtype == IPV4_PACKET)
				dot1q_offset = 4;
			else
				continue;

			if (ntohl(*((uint32_t *)(packet_buf + dot1q_offset + DST_IP_OFFSET))) != DST_IP_FILTER)
				continue;

			uint16_t last_ip_id = ntohs(*((uint16_t *)(packet_buf + dot1q_offset + IP_ID_OFFSET)));
			if ((uint16_t)(prev_ip_id + 0x0001) != last_ip_id)
			{
				printf("<WARN> IP_ID sequence number gap previous/latest: %d/%d \n", prev_ip_id, last_ip_id);
			}
			prev_ip_id = last_ip_id;

			st_best_depth_update *best_depth_update = (st_best_depth_update *)(packet_buf + dot1q_offset + PAYLOAD_OFFSET);
            		uint16_t decimal = best_depth_update->decimal;

#if 1
            if (!header_printed) {
                printf("合约代码, 时间戳.微秒, 最新价格, 成交量, 持仓量, 总成交额 | 买五档(价格_成交量_委托量), 买四档, 买三档, 买二档, 买一档 | 卖一档, 卖二档, 卖三档, 卖四档, 卖五档\n");
                printf("------------------------------------------------------------------------------------------------------------------------------------\n");
                header_printed = 1;
            }

            printf("%s, %d.%.6d, %.2f, %d, %d, %.2lf | %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d | %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d, %.2f_%d_%d\n",
                   best_depth_update->contract_id,

                   best_depth_update->update_time,
                   best_depth_update->update_time_usec,

                   (float)best_depth_update->last_price / decimal,
                   best_depth_update->volumn,
                   best_depth_update->open_interest,
                   (double)best_depth_update->turnover / decimal,

                   (float)best_depth_update->bid5_price / decimal,
                   (uint32_t)best_depth_update->bid5_volumn, 
                   (uint32_t)best_depth_update->bid5_qty,

                   (float)best_depth_update->bid4_price / decimal,
                   (uint32_t)best_depth_update->bid4_volumn,
                   (uint32_t)best_depth_update->bid4_qty,


                   (float)best_depth_update->bid3_price / decimal,
                   (uint32_t)best_depth_update->bid3_volumn,
                   (uint32_t)best_depth_update->bid3_qty,


                   (float)best_depth_update->bid2_price / decimal,
                   (uint32_t)best_depth_update->bid2_volumn,
                   (uint32_t)best_depth_update->bid2_qty,


                   // ntohl((uint32_t)best_depth_update->bid1) & 0x03ffffff, // price
                   // ntohl(best_depth_update->bid1 >> 32) & 0x00000fff, // qty
                   // (ntohl(best_depth_update->bid1 >> 32) & 0xfffff000) >> 12, // volumn
                   (float)best_depth_update->bid1_price / decimal,
                   (uint32_t)best_depth_update->bid1_volumn,
                   (uint32_t)best_depth_update->bid1_qty,

                   (float)best_depth_update->ask1_price / decimal,
                   (uint32_t)best_depth_update->ask1_volumn,
                   (uint32_t)best_depth_update->ask1_qty,

                   (float)best_depth_update->ask2_price / decimal,
                   (uint32_t)best_depth_update->ask2_volumn,
                   (uint32_t)best_depth_update->ask2_qty,

                   (float)best_depth_update->ask3_price / decimal,
                   (uint32_t)best_depth_update->ask3_volumn,
                   (uint32_t)best_depth_update->ask3_qty,

                   (float)best_depth_update->ask4_price / decimal,
                   (uint32_t)best_depth_update->ask4_volumn,
                   (uint32_t)best_depth_update->ask4_qty,


                   (float)best_depth_update->ask5_price / decimal,
                   (uint32_t)best_depth_update->ask5_volumn,
                   (uint32_t)best_depth_update->ask5_qty

            );

#endif
        }
    }

	return 0;
}
